import datetime
from flask import current_app, jsonify, g, request, session
from ihome import redis_store, constants, db
from ihome.api_1_0 import api
from ihome.models import Area, House, Facility, HouseImage, Order
from ihome.response_code import RET
from ihome.utils.commons import login_required
from ihome.utils.image_storage import storage


# 127.0.0.1:5000/api/v1.0/houses
@api.route('/houses', methods=["GET"])
def house_list():
    """房屋搜索"""

    """
    1.获取参数
        1.1 aid:区域id  sd(start_day):开始日期  ed(end_day):结束日期  sk(sort_key):关键字排序（分别有booking(订单量)、price-inc(低到高)、price-des(高到低)、p(page)页数不传默认为1）
    2.校验参数
    3.逻辑处理
    4.返回值
    """

    # 1.获取参数
    #     1.1 aid:区域id  sd(start_day):开始日期  ed(end_day):结束日期  sk(sort_key):关键字排序、p(page)页数不传默认为1）
    args = request.args
    aid = args.get('aid', '')
    start_day = args.get('sd', '')
    end_day = args.get('ed', '')
    # booking(订单量)、price - inc(低到高)、price - des(高到低)
    sort_key = args.get('sk', 'new')
    page = args.get('p', 1)

    # 打印数据（测试用）
    print("aid=%s, start_day=%s, end_day=%s, sort_key=%s, page=%s" % (aid, start_day, end_day, sort_key, page))

    # 2.校验参数
    try:
        page = int(page)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg='参数错误')

    # 结束时间应该大于开始时间
    try:
        start_date = None
        end_date = None
        if start_day:
            start_date = datetime.datetime.strptime(start_day, 'Y%-m%-d%')
        if end_day:
            end_date = datetime.datetime.strptime(end_day, 'Y%-m%-d%')
        # 如果开始时间大于或者等于结束时间，就报错
        if start_date and end_date:
            assert start_date < end_date, Exception("结束日期必须大于开始时间")
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg='参数错误')

    try:
        redis_key = "houses_%s_%s_%s_%s" % (aid, start_day, end_day, sort_key)
        response_data = redis_store.hget(redis_key, page)
        if response_data:
            current_app.logger.info('load data from redis')
            return jsonify(errno=RET.OK, errmsg='获取成功', data=eval(response_data))
    except Exception as e:
        current_app.logger.error(e)

    # 3.逻辑处理
    filters = []

    # 如果区域id存在
    if aid:
        filters.append(House.area_id == aid)

    # 定义数组保存冲突的订单
    conflict_order = None
    if start_date and end_date:
        # 如果订单的开始时间 < 结束时间 and 订单的结束时间 > 开始时间
        conflict_order = Order.query.filter(Order.begin_date < end_date, Order.end_date > start_date)
    elif start_date:
        # 订单的结束时间 > 开始时间
        conflict_order = Order.query.filter(Order.end_date > start_date)
    elif end_date:
        # 订单的开始时间 < 结束时间
        conflict_order = Order.query.filter(Order.begin_date < end_date)

    if conflict_order:
        # 取到冲突订单的房屋id
        conflict_house_id = [order.house_id for order in conflict_order]
        # 添加条件：查询出来的房屋不包括冲突订单中的房屋id
        filters.append(House.id.notin_(conflict_house_id))

    # --------------------按条件排序查询房屋数据--------------------
    if sort_key == "booking":
        # 订单量从高到低
        houses_query = House.query.filter(*filters).order_by(House.order_count.desc())
    elif sort_key == "price-inc":
        # 价格从低到高
        houses_query = House.query.filter(*filters).order_by(House.price)
    elif sort_key == "price-des":
        # 价格从高到低
        houses_query = House.query.filter(*filters).order_by(House.price.desc())
    else:
        # 默认以最新的排序
        houses_query = House.query.filter(*filters).order_by(House.create_time.desc())

    # 使用paginate进行分页
    house_pages = houses_query.paginate(page, constants.HOUSE_LIST_PAGE_CAPACITY, False)
    # 获取当前页对象
    houses = house_pages.items
    # 获取总页数
    total_page = house_pages.pages

    # 将查询结果转成字符串
    houses_dict = []
    for house in houses:
        houses_dict.append(house.to_basic_dict())

    # 提示 response_data 用于缓存
    response_data = {"total_page": total_page, "houses": houses_dict}
    # 如果当前page小于总页数,则表明有数据
    if page <= total_page:
        try:
            # 生成缓存用的key
            redis_key = "houses_%s_%s_%s_%s" % (aid, start_day, end_day, sort_key)
            # 获取 redis_store 的 pipeline 对象,其可以一次可以做多个redis操作
            pipe = redis_store.pipeline()
            # 开启事务
            pipe.multi()
            # 缓存数据
            pipe.hset(redis_key, page, response_data)
            # 设置保存数据的有效期
            pipe.expire(redis_key, constants.HOUSE_LIST_REDIS_EXPIRES)
            # 提交事务
            pipe.execute()
        except Exception as e:
            current_app.logger.error(e)

    # 4.返回值
    return jsonify(errno=RET.OK, errmsg='请求成功', data=response_data)


@api.route('/houses/index')
def house_index():
    """
    首页房屋推荐展示
    :return:
    """

    # 先从redis中加载数据
    try:
        house_info = redis_store.get('home_page_house_info')
    except Exception as e:
        current_app.logger.error(e)
        house_info = None
    if house_info:
        return jsonify(errno=RET.OK, errmsg='OK', data=eval(house_info))

    # 从数据库中加载数据
    try:
        houses = House.query.order_by(House.order_count.desc()).limit(constants.HOME_PAGE_MAX_HOUSES).all()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据查询失败')

    # 如果数据库中没有数据
    if not houses:
        return jsonify(errno=RET.NODATA, errmsg='未查询到数据')

    # 拼接到数组中
    houses_dict = []
    for house in houses:
        houses_dict.append(house.to_basic_dict())

    # 缓存到redis中
    try:
        redis_store.setex('home_page_house_info', constants.HOME_PAGE_DATA_REDIS_EXPIRES, houses_dict)
    except Exception as e:
        current_app.logger.error(e)

    # 返回数据
    return jsonify(errno=RET.OK, errmsg='OK', data=houses_dict)


@api.route('/houses/<house_id>')
def house_detail(house_id):
    """
    房屋详情
    :param house_id:
    :return:
    """

    # 判断参数是否有值
    if not house_id:
        return jsonify(errno=RET.PARAMERR, errmsg="参数缺失")

    user_id = session.get('user_id', -1)

    # 先从redis缓存中取
    try:
        house_info = redis_store.get('house_info_' + house_id)
    except Exception as e:
        current_app.logger.error(e)
        house_info = None

    # 如果有值，那么返回数据
    if house_info:
        current_app.logger.info('get house info from redis')
        return jsonify(errno=RET.OK, errmsg='OK', data={"user_id": user_id, "house": eval(house_info)})

    # 没有从缓存中取到，再查询数据库
    house = House.query.filter_by(id=house_id).first()
    if not house:
        return jsonify(errno=RET.NODATA, errmsg="未查询到房屋信息")

    # 将数据缓存到redis中
    house_dict = house.to_full_dict()
    try:
        redis_store.setex('house_info_' + house_id, constants.HOUSE_DETAIL_REDIS_EXPIRE_SECOND, house_dict)
    except Exception as e:
        current_app.logger.error(e)

    # 返回数据
    return jsonify(errno=RET.OK, errmsg='OK', data={"user_id": user_id, "house": house_dict})


@api.route('/houses/<int:house_id>/images', methods=['POST'])
@login_required
def upload_house_pic(house_id):
    """
    上传房源图片
    :param house_id:房源id
    :return:
    """

    # 取到图片文件
    image_file = request.files.get('house_image')
    if not image_file:
        return jsonify(errno=RET.PARAMERR, errmsg="未选择图片")

    # 查询房屋数据
    try:
        house = House.query.filter_by(id=house_id).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询房屋数据失败")
    if not house:
        return jsonify(errno=RET.NODATA, errmsg="未查询到对应房屋")

    # 使用七牛云上传图片
    image_data = image_file.read()
    try:
        image_name = storage(image_data)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg="上传图片失败")

    # 判断房屋是否有主图片，如果没有，则设置
    if not house.index_image_url:
        house.index_image_url = image_name
        db.session.add(house)

    # 生成房屋图片模型并保存至数据库
    house_image = HouseImage()
    house_image.house_id = house_id
    house_image.url = image_name

    try:
        db.session.add(house_image)
        db.session.commit()
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="保存房屋图片失败")

    # 返回数据
    image_url = constants.QINIU_DOMIN_PREFIX + image_name
    return jsonify(errno=RET.OK, errmsg="OK", data={'url': image_url})


@api.route('/houses', methods=['POST'])
@login_required
def save_new_house():
    """发布房源"""

    # 获取用户id
    user_id = g.user_id

    # 获取前端发送过来的json数据
    json_dict = request.get_json()
    if not json_dict:
        return jsonify(errno=RET.PARAMERR, errmsg="请输入参数")

    title = json_dict.get('title')
    price = json_dict.get('price')
    address = json_dict.get('address')
    area_id = json_dict.get('area_id')
    room_count = json_dict.get('room_count')
    acreage = json_dict.get('acreage')
    unit = json_dict.get('unit')
    capacity = json_dict.get('capacity')
    beds = json_dict.get('beds')
    deposit = json_dict.get('deposit')
    min_days = json_dict.get('min_days')
    max_days = json_dict.get('max_days')

    if not all([title, price, address, area_id, room_count, acreage, unit, capacity, beds, deposit, min_days, max_days]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数缺失")

    try:
        price = int(float(price) * 100)
        deposit = int(float(deposit) * 100)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg="参数错误")

    # 设置数据到模型
    house = House()
    house.user_id = user_id
    house.area_id = area_id
    house.title = title
    house.price = price
    house.address = address
    house.room_count = room_count
    house.acreage = acreage
    house.unit = unit
    house.capacity = capacity
    house.beds = beds
    house.deposit = deposit
    house.min_days = min_days
    house.max_days = max_days

    # 设置设施信息
    facility = json_dict.get('facility')
    if facility:
        facilities = Facility.query.filter(Facility.id.in_(facility)).all()
        house.facilities = facilities

    try:
        db.session.add(house)
        db.session.commit()
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="保存房屋信息失败")

    return jsonify(errno=RET.OK, errmsg="OK", data={'house_id': house.id})


@api.route('/areas', methods=['GET'])
def get_areas_info():
    """
    查询城区信息数据
    :return:
    """

    # 先从缓存中去取，如果缓存中没有，再去数据库中取
    try:
        areas = redis_store.get('area_info')
    except Exception as e:
        current_app.logger.error(e)
        areas = None

    # 如果不为空，做查询操作
    if areas:
        return jsonify(errno=RET.OK, errmsg="获取成功", data=eval(areas))

    # 从数据库取
    try:
        areas = Area.query.all()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="获取城区信息失败")

    # 因为查询出来的全是对象实例，不能直接进行JSON转化
    areas_list = []
    for area in areas:
        areas_list.append(area.to_dict())

    # 存储json_areas数据到redis缓存中
    try:
        redis_store.setex('area_info', constants.AREA_INFO_REDIS_EXPIRES, areas_list)
    except Exception as e:
        current_app.logger.error(e)

    return jsonify(errno=RET.OK, errmsg="获取成功", data=areas_list)
